package crmdna.counter;

import static crmdna.common.OfyService.ofy;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.googlecode.objectify.VoidWork;

import crmdna.common.APIException;
import crmdna.common.APIResponse.Status;
import crmdna.common.Utils;

class CounterCore {
	Random random = new Random();
	int numShards; // once set for a counter cannot be changed

	protected CounterCore(int numShards) {
		if (numShards < 0)
			Utils.throwIncorrectSpecException("numShards cannot be negative");

		final int MAX_SHARDS = 100;
		if (numShards > MAX_SHARDS)
			Utils.throwIncorrectSpecException("Only [" + MAX_SHARDS
					+ "] allowed");

		this.numShards = numShards;
	}

	private String getShardKey(String counterName, int shardIndex) {
		return counterName + "_shard" + shardIndex;
	}

	public void increment(final String namespace, String counterName,
			final int n) {
		int shardIndex = random.nextInt(numShards);

		final String key = getShardKey(counterName, shardIndex);

		final int MAX_KEY_LENGTH = 100;
		if (key.length() > MAX_KEY_LENGTH)
			throw new APIException(Status.ERROR_RESOURCE_INCORRECT,
					"Counter key [" + key + "] is more than [" + MAX_KEY_LENGTH
							+ "] chars");

		ofy(namespace).transact(new VoidWork() {

			@Override
			public void vrun() {
				ShardEntity se = ofy(namespace).load().type(ShardEntity.class)
						.id(key).get();
				if (se == null) {
					se = new ShardEntity();
					se.key = key;
					se.count = n;
				} else {
					se.count += n;
				}

				ofy(namespace).save().entity(se);
			}
		});
	}

	public long getCount(String namespace, String counterName) {
		List<String> keys = new ArrayList<>();
		for (int shardIndex = 0; shardIndex < numShards; shardIndex++) {
			keys.add(getShardKey(counterName, shardIndex));
		}

		Map<String, ShardEntity> map = ofy(namespace).load()
				.type(ShardEntity.class).ids(keys);

		long count = 0;
		for (String key : map.keySet()) {
			ShardEntity se = map.get(key);
			if (se != null)
				count += se.count;
		}

		return count;
	}
}
